<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.7.7/joint.css"/>
</head>
<body>
<!-- content -->
<div>
    <button></button>
</div>
<div id="myholder"></div>

<!-- dependencies -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.4.1/backbone.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.7.7/joint.js"></script>
<script src="joint.layout.treeLayout.js"></script>
<script src="./graphlib.min.js"></script>
<script src="./dagre.min.js"></script>
<!-- code -->
<script type="module">
  // import { data } from "./data.mjs";
  import {data} from "./data1.mjs";
  import {billGraph, reversedBillGraph, billTypeGraph} from "./graph.mjs";

  var namespace = joint.shapes;
  var graph = new joint.dia.Graph({}, {cellNamespace: namespace});

  graph.on('remove', function(cell) {
    const cellType = cell.attributes.type
    const data = cell.attributes.attrs.data
    if (cellType !== 'standard.Link') {
      // 关闭单据类型时，要把下属的单据移除，以及打开的上下游单据移除
      const id = data.id
      openDownBill = openDownBill.filter((bill) => bill !== id)
      // TODO 移除上游单据
      // 移除下游单据
      billGraph.edges[id].forEach((targetId) => {
        if (vertices[targetId]) {
          vertices[targetId].remove()
          vertices[targetId] = null
        }
      })
    }
  })

  graph.on('add', function(cell) {
    const cellType = cell.attributes.type
    const data = cell.attributes.attrs.data
    if (cellType !== 'standard.Link' && data?.type === 'bill') {
      const id = data.id
      // TODO 关闭其他 bill type

    }
  })

  let openBillType = {
    'ApPayment': true,
    'Payment': true,
  }
  let openDownBill = [
    // '0EXQ6461URL00EE',
  ]
  let openUpBill = []

  const position = {
    main: {
      x: 200,
      y: 100,
      width: 260,
      height: 40,
    },
    padding: {
      x: 40,
      y: 30,
    }
  }
  let rect = makeMainRect();
  var paper = new joint.dia.Paper({
    el: document.getElementById('myholder'),
    model: graph,
    width: 3600,
    height: 3600,
    cellViewNamespace: namespace
  });

  paper.on('cell:pointerclick', function (cellView) {
    console.log(cellView, cellView.model.attributes.attrs.data)
    var isElement = cellView.model.isElement();
    if (!isElement) {
      return
    }
    const data = cellView.model.attributes.attrs.data
    if (data.type === 'billType') {
      openBillType[data.name] = !openBillType[data.name];
      Object.entries(openBillType).forEach(([billType, value]) => {
        if (value) {
          drawBillGraph(billType, vertices[billType])
        } else {
          // 反向操作，把相关元素移除graph
          billTypeGraph.edges[billType].forEach((bill_vertex) => {
            if (vertices[bill_vertex]) {
              vertices[bill_vertex].remove()
              vertices[bill_vertex] = null
            }
          })
        }
      })
    } else if (data.type === 'bill') {
      const exist = openDownBill.some(i => i === data.id)
      if (exist) { // 当前单据以打开，从openDownBill数组中移除，并将边移除
        openDownBill = openDownBill.filter(i => i !== data.id)
        billGraph.edges[data.id].forEach((target) => {
          if (vertices[target]) {
            vertices[target].remove()
            vertices[target] = null
          }
        })
      } else {
        openDownBill.push(data.id)
        openDownBill.forEach(bill => {
          if (vertices[bill]) {
            openDown(bill, vertices[bill])
          } else {
            console.error(bill)
          }
        })
      }
    }
  });

  joint.dia.Element.define('standard.Rectangle', {
    attrs: {
      body: {
        // rx: 10,
        // ry: 10,
        width: 'calc(w)',
        height: 'calc(h)',
        strokeWidth: 2,
        stroke: '#000000',
        fill: '#FFFFFF'
      },
      label: {
        textVerticalAnchor: 'bottom',
        textAnchor: 'right',
        x: '0',
        y: 'calc(0.5*h)',
        fontSize: 14,
        fill: '#333333'
      },
      card_type: {
        textVerticalAnchor: 'bottom',
        textAnchor: 'end',
        x: 'calc(1*w)',
        y: 'calc(0.5*h)',
        fontSize: 14,
        fill: 'blue'
      },
      up_g: {
        width: '40',
        height: '20',
        strokeWidth: 2,
        stroke: '#000000',
        fill: '#FFFFFF'
      },
      up: {
        width: '40',
        height: '20',
        x: '0',
        y: 'calc(0.5*h)',
        strokeWidth: 2,
        stroke: '#000000',
        fill: '#FFFFFF'
      },
      up_label:{
        textVerticalAnchor: 'middle',
        textAnchor: 'right',
        refX: '0',
        refY: '100%',
        refX2: '10',
        refY2: '-14',
        fontSize: 14,
        fill: '#333333',
        text: 'up',
      },
      down: {
        width: '40',
        height: '20',
        x: 'calc(1*w - 40)',
        y: 'calc(0.5*h)',
        strokeWidth: 2,
        stroke: '#000000',
        fill: '#FFFFFF'
      },
      down_label:{
        textVerticalAnchor: 'end',
        textAnchor: 'right',
        refX: '100%',
        refY: '100%',
        refX2: '-40',
        refY2: '-5',
        fontSize: 14,
        fill: '#333333',
        text: 'down',
      },
    }
  }, {
    markup: [
      {
        tagName: 'rect',
        selector: 'body',
      },
      {
        tagName: 'g',
        selector: 'up_g',
        children: [
          {
            tagName: 'rect',
            selector: 'up',
          },
          {
            tagName: 'text',
            selector: 'up_label'
          },
        ]
      },

      {
        tagName: 'rect',
        selector: 'down',
        children: [

        ]
      },
      {
        tagName: 'text',
        selector: 'down_label'
      },
      {
        tagName: 'text',
        selector: 'label'
      },
      {
        tagName: 'text',
        selector: 'card_type',

      },
    ]
  });

  function makeMainRect() {

    const rect = new joint.shapes.standard.Rectangle({z: 2});
    rect.position(position.main.x, position.main.y);
    rect.resize(position.main.width, position.main.height);
    rect.attr({
      body: {
        fill: 'blue'
      },
      label: {
        text: '0',
        fill: 'white'
      }
    });
    rect.attr('label/text', data.curObject.id);
    rect.addTo(graph);
    return rect;
  }


  // graph
  const vertices = {}

  function drawBillTypeGraph() {
    vertices[data.curObject.id] = rect
    billTypeGraph.edges[data.curObject.id].forEach((target, i) => {
      var rect2 = rect.clone();
      const basePosition = rect.position();
      rect2.position(basePosition.x + (position.main.width + position.padding.x) * (i + 1), basePosition.y);
      rect2.attr('label/text', target + ':' + billTypeGraph.edges[target].length);
      rect2.attr('data', {name: target, type: 'billType'})
      rect2.addTo(graph);
      vertices[target] = rect2
    })
    billTypeGraph.edges[data.curObject.id].forEach((target) => {
      const link = makeLine();
      link.source(vertices[data.curObject.id])
      link.target(vertices[target])
      link.addTo(graph);
    })
  }

  function drawBillGraph(billType, billTypeEle, billTypeIndex) {
    billTypeGraph.edges[billType].forEach((bill_vertex, i) => {
      const rect2 = makeRect(bill_vertex)
      // y轴方向
      const basePosition = billTypeEle.position()
      rect2.position(basePosition.x, basePosition.y + (position.main.height + position.padding.y) * (i + 1));
      vertices[bill_vertex] = rect2
      // 不需要连线
      // const link = makeLine();
      // if (!billTypeEle) {
      //   console.log(billType, billTypeEle)
      // }
      // link.source(billTypeEle)
      // link.target(rect2)
      // link.addTo(graph);
    })
  }

  function openDown(bill, billEle) {
    billGraph.edges[bill].forEach((target, i) => {
      let rect2 = makeRect(target)
      vertices[target] = rect2

      // 根据target的objectType找到对应的element，
      // ？获取type的position
      // ？通知type打开某些单据
      const basePosition = billEle.position()
      const x = basePosition.x + (position.main.width + position.padding.x) * (i + 1)
      rect2.position(x, basePosition.y)

      const link = makeLine();
      link.source(billEle)
      link.target(rect2)
      link.addTo(graph);
    })
  }

  function openUp(bill, billEle) {
    reversedBillGraph.edges[bill].forEach((target) => {
      var rect2
      if (vertices[target]) {
        rect2 = vertices[target]
      } else {
        rect2 = rect.clone();
        const down_count = billGraph.edges[target].length
        const up_count = reversedBillGraph.edges[target].length
        rect2.attr('label/text', up_count + ':' + target + ':' + down_count);
        rect2.addTo(graph);
      }
      const link = makeLine();
      link.source(billEle)
      link.target(rect2)
      link.addTo(graph);
    })
  }

  function drawAll() {
    const vertices = {}
    vertices[data.curObject.id] = rect
    console.log(billGraph)
    billGraph.vertices.forEach((vertex) => {
      var rect2 = rect.clone();
      rect2.attr('label/text', vertex);
      rect2.addTo(graph);
      vertices[vertex] = rect2
    })

    Object.entries(billGraph.edges).forEach(([source, target]) => {
      if (target.length > 0) {
        for (let i = 0; i < target.length; i++) {
          const link = makeLine();
          link.source(vertices[source])
          link.target(vertices[target[i]])
          link.addTo(graph);
        }
      }
    })
  }

  // drawAll()
  drawBillTypeGraph()
  Object.entries(openBillType).forEach(([billType, value], billTypeIndex) => {
    if (value) {
      drawBillGraph(billType, vertices[billType], billTypeIndex)
    }
  })
  openDownBill.forEach(bill => {
    if (!vertices[bill]) {
      console.log(bill)
    }
    openDown(bill, vertices[bill])
  })
  // openUpBill.forEach(bill => {
  //   openUp(bill, vertices[bill])
  // })

  function makeLine() {
    return new joint.shapes.standard.Link({z: 1})
  }

  function makeRect(bill_id) {
    vertices[bill_id] = null
    var rect2 = rect.clone();
    const down_count = billGraph.edges[bill_id].length
    const up_count = reversedBillGraph.edges[bill_id].length
    const billInfo = data.objects.find(i => i.id === bill_id)
    rect2.attr('label/text', up_count + ':' + billInfo?.cardFieldInfo?.code + ':' + down_count);
    rect2.attr('data', {id: bill_id, type: 'bill'})
    rect2.addTo(graph);
    return rect2
  }

</script>
</body>
</html>
